﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Canroc.Net.Application.SysResourceService.Dto;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using System.Reflection;
using System.Xml.Linq;

// ReSharper disable once CheckNamespace
namespace Canroc.Net.Application.SysResourceService;

/// <summary>
///     系统资源应用服务
/// </summary>
[ApiDescriptionSettings("System")]
public partial class SysResourceAppService(
    IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
    SqlSugarRepository<SysResource> resourceRep) : IDynamicApiController, ITransient
{
    /// <summary>
    ///     同步本地资源接口到数据库
    /// </summary>
    /// <returns></returns>
    [Route("sync")]
    public async Task<bool> SyncLocalInterfaceAsync()
    {
        // 获取本地所有资源接口
        var localInterfaces = GetAllInterface() ?? throw Oops.Oh(ErrorCodeEnum.NotData);

        // 清除数据库中的所有资源接口
        _ = await resourceRep.DeleteAsync(i => true);
        return await resourceRep.InsertRangeAsync(localInterfaces);
    }

    /// <summary>
    ///     分页: 系统资源集合
    /// </summary>
    /// <param name="query">请求文件</param>
    /// <returns></returns>
    public async Task<PageModel<ResourceOutput>> GetPageAsync([FromQuery] ResourcePageQuery query)
    {
        return await resourceRep
            .AsQueryable()
            .WhereIF(!query.Method.IsNullOrEmpty(), r => r.Method == query.Method)
            .WhereIF(query.IsAuthorized.HasValue, r => r.IsAuthorized == query.IsAuthorized)
            .WhereIF(!query.Route.IsNullOrEmpty(), r => r.Route.Contains(query.Route))
            .WhereIF(!query.Controller.IsNullOrEmpty(), r => r.Controller.Contains(query.Controller))
            .OrderByPropertyName(query.OrderField, query.IsAscending ? OrderByType.Asc : OrderByType.Desc)
            .Select<ResourceOutput>()
            .ToPageAsync(query.PageIndex, query.PageSize);
    }

    /// <summary>
    ///     获取资源列表
    /// </summary>
    /// <returns></returns>
    public async Task<List<ResourceGroupOutput>?> GetResourceListAsync()
    {
        var result = await resourceRep.AsQueryable().Where(r => r.IsAuthorized)
            .OrderBy(r => new { r.Controller, r.Method, r.Action }).ToListAsync();
        // 对结果进行分组，按照控制器进行分组
        return result.GroupBy(r => r.Controller).Select(g => new ResourceGroupOutput
        {
            Controller = g.Key,
            Description = g.First().ControllerDescription,
            Children = g.Select(r => new ResourceItemOutput
            {
                Method = r.Method, Route = r.Route, Description = r.Description
            }).ToList()
        }).ToList();
    }

    #region 私有函数

    /// <summary>
    ///     获取所有接口
    /// </summary>
    /// <returns></returns>
    [NonAction]
    private List<SysResource>? GetAllInterface()
    {
        // 获取所有 Action 列表
        var actionDescriptors = actionDescriptorCollectionProvider.ActionDescriptors.Items;
        if (!actionDescriptors.Any()) return null;

        // 读取当前方法所属的程序集并加载xml文档
        var assembly = Assembly.GetExecutingAssembly();
        var xmlPath = Path.Combine(AppContext.BaseDirectory,
            $"{assembly.ManifestModule.Name.Replace(".dll", "")}.xml");
        if (!File.Exists(xmlPath)) return null;

        // 读取xml文档
        var xmlDoc = XDocument.Load(xmlPath);
        return actionDescriptors
            .OfType<ControllerActionDescriptor>()
            .Select(actionDescriptor =>
            {
                // 判断是否有 AllowAnonymous 特性
                var isAuthorize = !actionDescriptor.EndpointMetadata.Any(m => m is AllowAnonymousAttribute);

                // 获取请求的方法
                var method = actionDescriptor.MethodInfo;
                // 获取控制器的注释
                var controllerName = method.DeclaringType!.FullName!;
                var controllerElement = xmlDoc.Root?.Element("members")?.Elements()
                    .FirstOrDefault(e => e.Attribute("name")?.Value == $"T:{controllerName}");
                var controllerSummary = controllerElement?.Element("summary")?.Value.Trim().Replace("\n", "");

                // 获取方法的注释
                var parameterTypeString =
                    string.Join(", ", method.GetParameters().Select(p => p.ParameterType.FullName));
                var actionName =
                    $"{method.DeclaringType!.FullName}.{method.Name}{(string.IsNullOrEmpty(parameterTypeString) ? "" : $"({parameterTypeString})")}";
                var actionElement = xmlDoc.Root?.Element("members")?.Elements()
                    .FirstOrDefault(e => e.Attribute("name")?.Value == $"M:{actionName}");
                var actionSummary = actionElement?.Element("summary")?.Value.Trim().Replace("\n", "");

                // 获取路由地址
                var route = actionDescriptor.AttributeRouteInfo?.Template;

                // 获取 HttpMethod
                var httpMethod = actionDescriptor.ActionConstraints?.OfType<HttpMethodActionConstraint>()
                    .FirstOrDefault()?.HttpMethods.First();

                return new SysResource
                {
                    Method = httpMethod ?? string.Empty,
                    Route = route ?? string.Empty,
                    Controller = method.DeclaringType?.Name ?? string.Empty,
                    ControllerDescription = controllerSummary ?? string.Empty,
                    Action = method.Name,
                    FullName = actionName,
                    Description = actionSummary ?? string.Empty,
                    IsAuthorized = isAuthorize
                };
            })
            .ToList();
    }

    #endregion
}